import QUANTAXIS as QA
import pandas as pd
import numpy as np
import tushare as ts
import datetime


Q_data = pd.date_range(
    '1/1/1996',
    '12/31/2023',
    freq='Q-MAR'
).astype(str).tolist()

fin_columns = [
        'date',
        'code',
        'price',            # 股价
        'total_assets', # 总市值 = 当前股价 * 总股本（238）
        'ln_capital',   # 市值 ln_capital 总市值取对数
        'PE',           # 估值 PE 市盈率（动态）
        'PB',           # 估值 PB 市净率
        'PS',           # 估值 PS 市销率 总市值/营业收入（TTM）（074 ? 283）/ 1
        'NCFP',         # 估值 NCFP 净现金流（TTM）/总市值 1 =
        'OCFP',         # 估值 OCFP 经营性现金流（TTM）/总市值 1 = 219每股经营性现金流 / 总股本（238）
        'G_PE',         # 估值 G/PE 净利润（TTM）同比增长率(184)/PE_TTM 1
        'Sales_G_q',   # 成长 Sales_G_q 营业收入（最新财报，YTD）同比增长率(183) 1
        'Profit_G_q',  # 成长 Profit_G_q 净利润（最新财报，YTD）同比增长率(184) 1
        'OCF_G_q',     # 成长 OCF_G_q 经营性现金流（最新财报，YTD）同比增长率 1
                       # = （本期经营活动现金净流量(225)-上期经营活动现金净流量）/上期经营活动现金净流量*100%
        'ROE_G_q',     # 成长 ROE_G_q ROE（最新财报，YTD）(006与上期相比)同比增长率 1
        'ROE_q',                        # 财务质量 ROE_q ROE（最新财报，YTD）(006) 1
        'ROA_q',                        # 财务质量 ROA_q ROA（最新财报，YTD）1 资产回报率= 净利润（276）/总资产（040）
        'grossprofitmargin_q',          # 财务质量 grossprofitmargin_q 毛利率（最新财报，YTD）（202） 1
        'profitmargin_q',               # 财务质量 profitmargin_q 扣除非经常性损益后净利润率（最新财报，YTD）（206） 1
        'assetturnover_q',              # 财务质量 assetturnover_q 资产周转率（最新财报，YTD）（175） 1
        'operationcashflowratio_q',     # 财务质量 operationcashflowratio_q 经营性现金流/净利润（最新财报，YTD）（228） 1
        'financial_leverage',           # 杠杆 financial_leverage 总资产（040）/净资产 -1 净资产 = 每股净资产（004） * 总股本（238）
        'debtequityratio',              # 杠杆 debtequityratio 非流动负债（062）/净资产 -1
        'cashratio',                    # 杠杆 cashratio 现金比率（161） 1
        'currentratio',                 # 杠杆 currentratio 流动比率（159） 1
        ]
ind_columns = [
        'date',
        'code',
        'MACD',             # 技术 MACD 经典技术指标（释义可参考百度百科），长周期取 30 日，短周期取 10 日，计算 DEA 均线的周期（中周期）取 15日 -1
        'DEA',              # 技术 DEA -1
        'DIF',              # 技术 DIF -1
        'RSI',              # 技术 RSI 经典技术指标，周期取 20 日 -1
        'PSY',              # 技术 PSY 经典技术指标，周期取 20 日 -1
        'BIAS'              # 技术 BIAS 经典技术指标，周期取 20 日 -1
        'HAlpha',           # 动量反转 HAlpha 个股 60 个月收益与上证综指回归的截距项 -1
        'return_1',         # 动量反转 return_Nm 个股最近 N 个月收益率，N=1 -1
        'return_3',         # 动量反转 return_Nm 个股最近 N 个月收益率，N=3 -1
        'return_6',         # 动量反转 return_Nm 个股最近 N 个月收益率，N=6 -1
        'return_12',        # 动量反转 return_Nm 个股最近 N 个月收益率，N=12 -1
        'w_gt_return_1',    # 动量反转 w gt_return_Nm 个股最近 N 个月内用每日换手率乘以每日收益率求算术平均值，N=1 -1
        'w_gt_return_3',    # 动量反转 w gt_return_Nm 个股最近 N 个月内用每日换手率乘以每日收益率求算术平均值，N=3 -1
        'w_gt_return_6',    # 动量反转 w gt_return_Nm 个股最近 N 个月内用每日换手率乘以每日收益率求算术平均值，N=6 -1
        'w_gt_return_12',   # 动量反转 w gt_return_Nm 个股最近 N 个月内用每日换手率乘以每日收益率求算术平均值，N=12 -1
        'std_1',            # 波动率 std_Nm 个股最近 N个月的日收益率序列标准差，N=1 -1
        'std_3',            # 波动率 std_Nm 个股最近 N个月的日收益率序列标准差，N=3 -1
        'std_6',            # 波动率 std_Nm 个股最近 N个月的日收益率序列标准差，N=6 -1
        'std_12',           # 波动率 std_Nm 个股最近 N个月的日收益率序列标准差，N=12 -1
        'ln_price',         # 股价 ln_price 股价取对数 -1
        'beta',             # beta beta 个股 60 个月收益与上证综指回归的 beta -1
        'turn_1',           # 换手率 turn_Nm 个股最近 N个月内日均换手率（剔除停牌、涨跌停的交易日），N=1 -1
        'turn_3',           # 换手率 turn_Nm 个股最近 N个月内日均换手率（剔除停牌、涨跌停的交易日），N=3 -1
        'turn_6',           # 换手率 turn_Nm 个股最近 N个月内日均换手率（剔除停牌、涨跌停的交易日），N=6 -1
        'turn_12',          # 换手率 turn_Nm 个股最近 N个月内日均换手率（剔除停牌、涨跌停的交易日），N=12 -1
        ]

def get_month_factors(data, date, date_trade_datetime, listedAShares, month=1):
    stock_today = data.select_day(date_trade_datetime)
    date_start = QA.QA_util_get_recent_months(date, -month)
    date_month_day_1 = QA.QA_util_get_next_trade_date(date_start)
    # date_month_day_1 = QA.QA_util_get_month_day_1(date_start)  # 获取本月1日
    # while not QA.QA_util_is_trade(date_month_day_1, '000001', CLIENT):
    #     date_month_day_1 = QA.QA_util_get_next_trade_date(date_month_day_1)
    stock_month_day_1 = data.select_day(pd.to_datetime(date_month_day_1))
    rtn = (stock_today.CLOSE - stock_month_day_1.pre_close) / stock_month_day_1.pre_close

    stock_month_1 = data.select_time(start=pd.to_datetime(date_month_day_1), end=pd.to_datetime(date))
    series_w_gt_return_1 = stock_month_1.price_chg * stock_month_1.volume * 10000 / listedAShares
    w_gt_return = series_w_gt_return_1.groupby(level=1).apply(lambda x: x.mean())
    # exp_w_gt_return = (series_w_gt_return_1 * np.exp(-stock_month_1.trade_day_count / 4)).groupby(level=1).apply(
    #     lambda x: x.mean())
    exp_w_gt_return = 0
    std = stock_month_1.price_chg.groupby(level=1).apply(lambda x: x.std())
    stock_month_1_ls = pd.DataFrame({'hsl': stock_month_1.volume * 100 / listedAShares,
                                     'filter': stock_month_1.limit_and_stopped})
    stock_month_1_filter = stock_month_1_ls[stock_month_1_ls['filter'] == False]
    turn = stock_month_1_filter['hsl'].groupby(level=1).apply(lambda x: x.mean())

    # df_up = stock_month_1.data[stock_month_1.price_chg > 0]
    # psy = df_up.shape[0] / stock_month_1.data.shape[0]

    return pd.DataFrame({'return': rtn,
                         'w_gt_return': w_gt_return,
                         'exp_w_gt_return': exp_w_gt_return,
                         'std': std,
                         # 'psy': psy,
                         'turn': turn})

class AssessStocks():
    def __init__(self, codes, name='默认', start=None, end=None):
        self.codes = codes
        self.name = name
        if start == None:
            self.start = '1996-01-01'
        else:
            self.start = start
        if end == None:
            self.end = str(datetime.date.today())
        else:
            self.end = end
        self.fetch_stock_list()
        self.fetch_stock_day()
        self.fetch_financial()

    def __repr__(self):
        return '<Assess> %s' % self.name

    def fetch_financial(self):
        self.financial = QA.QA_fetch_financial_report_adv(self.codes, start=self.start, end=self.end)


    def fetch_stock_day(self):
        self.data = QA.QA_fetch_stock_day_adv(self.codes, self.start, self.end)
        self.data = self.data.to_qfq()
        date_trade = self.end
        if not QA.QA_util_if_trade(date_trade):
            date_trade = QA.QA_util_get_pre_trade_date(date_trade)
        self.date_trade_datetime = pd.to_datetime(date_trade)
        self.stock_today = self.data.select_day(self.date_trade_datetime)

    def fetch_stock_list(self):
        list = QA.QA_fetch_stock_list_adv()
        self.names = list.name[self.codes]
        # print(list)

    def evaluate(self):
        f = self._eval_financial()
        i = self._eval_indicator()
        df = pd.concat([f, i], axis=1)
        df.to_csv('eval.csv', encoding='gbk')
        return df

    def _eval_financial(self):
        codes = self.codes
        dates = self.financial.data.index.levels[0]
        date_Qn = dates[len(dates) - 1]
        last_year_fin = datetime.datetime(date_Qn.year - 1, date_Qn.month, date_Qn.day)
        date_Qn_pre = pd.to_datetime(last_year_fin)
        close = self.stock_today.CLOSE[codes]
        # if update_Q:
        totalCapital = self.financial.data.totalCapital[date_Qn][codes]  # 238 总股本
        totalValue = close * totalCapital
        netAssetsPerShare = self.financial.data.netAssetsPerShare[date_Qn][codes]  # 每股净资产（004）
        netAssets = netAssetsPerShare * totalCapital  # 净资产
        ROE = self.financial.data.netAssetsPerShare[date_Qn][codes]  # （006）
        totalAssets = self.financial.data.totalAssets[date_Qn][codes]  # 040 总资产
        totalNonCurrentLiabilities = self.financial.data.totalNonCurrentLiabilities[date_Qn][codes]  # 062
        netCashFlows = self.financial.data.netCashFlowsFromOperatingActivities[date_Qn][codes]  # 107经营活动产生的现金流量净额
        netProfitGrowthRate = self.financial.data.netProfitGrowthRate[date_Qn][codes]  # 184
        cutProfit = self.financial.data.netProfitAfterExtraordinaryGainsAndLosses[date_Qn][codes]  # 206
        netCashFlowPerShare = self.financial.data.netCashFlowPerShare[date_Qn][codes]  # 225
        operatingCashFlowPerShare = self.financial.data.operatingCashFlowPerShare[date_Qn][codes]  # 219
        self.listedAShares = self.financial.data.listedAShares[date_Qn][codes]  # 239 A股流通股本
        netProfit = self.financial.data.netProfitsBelongToParentCompanyOwner[date_Qn][codes]  # 276
        lastIncome = self.financial.data.lastYearOperatingIncome[date_Qn][codes] * 1e4  # 283

        pre_ROE = self.financial.data.netAssetsPerShare[date_Qn_pre][codes]  # （006）
        pre_netCashFlowPerShare = self.financial.data.netCashFlowPerShare[date_Qn_pre][codes]  # 225

        netProfit = netProfit * 4 / 3

        df_rtn = pd.DataFrame({
            'date': pd.to_datetime(self.end),
            'code': codes,
            'name': self.names,
            'price': close,            # 股价
            # 'totalValue': totalValue,  # 总市值 = 当前股价 * 总股本（238）
            'ln_value': np.log(totalValue),  # 市值 ln_capital 总市值取对数
            'PE': totalValue / netProfit,  # 估值 PE 市盈率（动态）
            'PB': close / netAssetsPerShare,  # 估值 PB 市净率
            'PS': totalValue / lastIncome,  # 估值 PS 市销率 总市值/营业收入（TTM）（074 ? 283）/ 1
            'NCFP': netCashFlows / totalValue,  # 估值 NCFP 净现金流（TTM）/总市值 1 =
            # 'OCFP': operatingCashFlowPerShare / totalCapital,  # 估值 OCFP 经营性现金流（TTM）/总市值 1 = 219每股经营性现金流 / 总股本（238）
            'G_PE': netProfitGrowthRate * totalValue / netProfit,  # 估值 G/PE 净利润（TTM）同比增长率(184)/PE_TTM 1
            # 'Sales_G_q': self.financial.data.operatingIncomeGrowth[date_Qn][codes],  # 成长 Sales_G_q 营业收入（最新财报，YTD）同比增长率(183) 1
            # 'Profit_G_q': netProfitGrowthRate,  # 成长 Profit_G_q 净利润（最新财报，YTD）同比增长率(184) 1
            # 'OCF_G_q': (netCashFlowPerShare - pre_netCashFlowPerShare) / pre_netCashFlowPerShare,  # 成长 OCF_G_q 经营性现金流（最新财报，YTD）同比增长率 1
            # = （本期经营活动现金净流量(225)-上期经营活动现金净流量）/上期经营活动现金净流量*100%
            'ROE_G_q': (ROE - pre_ROE) / pre_ROE,  # 成长 ROE_G_q ROE（最新财报，YTD）(006与上期相比)同比增长率 1
            'ROE_q': ROE,  # 财务质量 ROE_q ROE（最新财报，YTD）(006) 1
            'ROA_q': netProfit / totalAssets,  # 财务质量 ROA_q ROA（最新财报，YTD）1 资产回报率= 净利润（276）/总资产（040）
            # 'grossprofitmargin_q': self.financial.data.rateOfReturnOnGrossProfitFromSales[date_Qn][codes],  # 财务质量 grossprofitmargin_q 毛利率（最新财报，YTD）（202） 1
            'profitmargin_q': cutProfit / 1e8,  # 财务质量 profitmargin_q 扣除非经常性损益后净利润率（最新财报，YTD）（206） 1
            # 'assetturnover_q': self.financial.data.turnoverRatioOfTotalAssets[date_Qn][codes],  # 财务质量 assetturnover_q 资产周转率（最新财报，YTD）（175） 1
            # 'operationcashflowratio_q':self.financial.data.cashFlowRateAndNetProfitRatioOfOperatingActivities[date_Qn][codes],  # 财务质量 operationcashflowratio_q 经营性现金流/净利润（最新财报，YTD）（228） 1
            # 'financial_leverage': totalAssets / netAssets,  # 杠杆 financial_leverage 总资产（040）/净资产 -1 净资产 = 每股净资产（004） * 总股本（238）
            # 'debtequityratio': totalNonCurrentLiabilities / netAssets,  # 杠杆 debtequityratio 非流动负债（062）/净资产 -1
            # 'cashratio': self.financial.data.cashRatio[date_Qn][codes],  # 杠杆 cashratio 现金比率（161） 1
            # 'currentratio': self.financial.data.currentRatio[date_Qn][codes]
        })
        return df_rtn.set_index(['date', 'code'])
        # return df_rtn

    def _eval_indicator(self):
        codes = self.codes
        close = self.stock_today.CLOSE[codes]
        ln_price = np.log(close)
        df_macd = self.data.add_func(QA.QA_indicator_MACD, short=10, long=30, mid=15)
        df_macd_today_macd = df_macd.MACD[self.date_trade_datetime][codes]
        df_macd_today_dif = df_macd.DIF[self.date_trade_datetime][codes]
        df_macd_today_dea = df_macd.DEA[self.date_trade_datetime][codes]
        df_rsi = self.data.add_func(QA.QA_indicator_RSI)
        df_rsi1 = df_rsi.RSI1[self.date_trade_datetime][codes]
        # df_rsi2 = df_rsi.RSI2[self.date_trade_datetime][codes]
        # df_rsi3 = df_rsi.RSI3[self.date_trade_datetime][codes]
        # df_rsi_today = pd.DataFrame({'R1': df_rsi1, 'R2': df_rsi2, 'R3': df_rsi3})
        # df_good = (df_rsi_today.T < 20).all()
        df_kdj = self.data.add_func(QA.QA_indicator_KDJ)
        df_k = df_kdj.KDJ_K[self.date_trade_datetime][codes]
        df_d = df_kdj.KDJ_D[self.date_trade_datetime][codes]
        df_j = df_kdj.KDJ_J[self.date_trade_datetime][codes]
        df_kdj_today = pd.DataFrame({'K': df_k, 'D': df_d, 'J': df_j})
        df_kdj_good = (df_kdj_today.T < 20).all()
        df_bias = self.data.add_func(QA.QA_indicator_BIAS, N1=20, N2=20, N3=20)
        df_bias_today = df_bias.BIAS1[self.date_trade_datetime][codes]
        df_1 = get_month_factors(self.data, self.end, self.date_trade_datetime, self.listedAShares, month=1)
        df_3 = get_month_factors(self.data, self.end, self.date_trade_datetime, self.listedAShares, month=3)
        df_6 = get_month_factors(self.data, self.end, self.date_trade_datetime, self.listedAShares, month=6)
        df_12 = get_month_factors(self.data, self.end, self.date_trade_datetime, self.listedAShares, month=12)
        df_rtn = pd.DataFrame({
            'date': pd.to_datetime(self.end),
            'code': codes,
            'MACD': df_macd_today_macd,             # 技术 MACD 经典技术指标，长周期取 30 日，短周期取 10 日，计算 DEA 均线的周期（中周期）取 15日 -1
            'DEA': df_macd_today_dea,              # 技术 DEA -1
            'DIF': df_macd_today_dif,              # 技术 DIF -1
            # 'RSI_G': df_good,
            'RSI1': df_rsi1,
            # 'RSI2': df_rsi2,
            # 'RSI3': df_rsi3,
            'KDJ_K': df_k,
            'KDJ_D': df_d,
            'KDJ_J': df_j,
            'KDJ_G': df_kdj_good,
            'BIAS': df_bias_today              # 技术 BIAS 经典技术指标，周期取 20 日 -1
            # 'return_1': df_1['return'],
            # 'w_gt_return_1': df_1['w_gt_return'],
            # 'std_1': df_1['std'],
            # 'turn_1': df_1['turn'],
            # 'return_3': df_3['return'],
            # 'w_gt_return_3': df_3['w_gt_return'],
            # 'std_3': df_3['std'],
            # 'turn_3': df_3['turn'],
            # 'return_6': df_6['return'],
            # 'w_gt_return_6': df_6['w_gt_return'],
            # 'std_6': df_6['std'],
            # 'turn_6': df_6['turn'],
            # 'return_12': df_12['return'],
            # 'w_gt_return_12': df_12['w_gt_return'],
            # 'std_12': df_12['std'],
            # 'turn_12': df_12['turn']
                               })
        return df_rtn.set_index(['date', 'code'])
        # return df_rtn

start_date = '2016-06-01'
end_date = '2020-08-04'
hs300 = ts.get_hs300s()
sz500 = ts.get_zz500s()
all = pd.concat([hs300, sz500])
all_codes = all['code'].to_list()
# all_codes = ['000001', '600000', '000002']
ass = AssessStocks(all_codes, start=start_date, end=end_date)
ass.evaluate()